home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / MoreFiles 1.1.1 / DirectoryCopy.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-22  |  15.3 KB  |  430 lines  |  [TEXT/KAHL]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    DirectoryCopy: A robust, general purpose directory copy routine.
  5. **
  6. **    by Jim Luther, Apple Developer Technical Support
  7. **
  8. **    File:        DirectoryCopy.c
  9. **
  10. **    Copyright © 1992-1994 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  17. **    after having made changes. If you're going to re-distribute the source,
  18. **    we require that you make it clear in the source that the code was
  19. **    descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #ifndef __DIRECTORYCOPY__
  23. #include "DirectoryCopy.h"
  24. #endif
  25.  
  26.  
  27. /*****************************************************************************/
  28.  
  29. /* local constants */
  30.  
  31. enum
  32. {
  33.     dirCopyBigCopyBuffSize  = 0x00004000,
  34.     dirCopyMinCopyBuffSize  = 0x00000200
  35. };
  36.  
  37.  
  38. /*****************************************************************************/
  39.  
  40. /* local data structures */
  41.  
  42. /* The EnumerateGlobals structure is used to minimize the amount of
  43. ** stack space used when recursively calling CopyLevel and to hold
  44. ** global information that might be needed at any time. */
  45. struct EnumerateGlobals
  46. {
  47.     Ptr            copyBuffer;            /* pointer to buffer used for file copy operations */
  48.     long        bufferSize;            /* the size of the copy buffer */
  49.     CopyErrProcPtr errorHandler;    /* pointer to error handling function */
  50.     OSErr        error;                /* temporary holder of results - saves 2 bytes of stack each level */
  51.     Boolean        bailout;            /* set to true to by error handling function if fatal error */
  52.     short        destinationVRefNum;    /* the destination vRefNum */
  53.     Str63        itemName;            /* the name of the current item */
  54.     CInfoPBRec    myCPB;                /* the parameter block used for PBGetCatInfo calls */
  55. };
  56. typedef struct EnumerateGlobals EnumerateGlobals;
  57. typedef EnumerateGlobals *EnumerateGlobalsPtr;
  58.  
  59.  
  60. /* The PreflightGlobals structure is used to minimize the amount of
  61. ** stack space used when recursively calling GetLevelSize and to hold
  62. ** global information that might be needed at any time. */
  63. struct PreflightGlobals
  64. {
  65.     OSErr        result;                /* temporary holder of results - saves 2 bytes of stack each level */
  66.     Str63        itemName;            /* the name of the current item */
  67.     CInfoPBRec    myCPB;                /* the parameter block used for PBGetCatInfo calls */
  68.  
  69.     long        dstBlksPerAllocBlk;    /* the number of 512 byte blocks per allocation block on destination */
  70.     long        allocBlksNeeded;    /* the total number of allocation blocks needed  */
  71.  
  72.     long         tempBlocks;            /* temporary storage for calculations (save some stack space)  */
  73. };
  74. typedef struct PreflightGlobals PreflightGlobals;
  75. typedef PreflightGlobals *PreflightGlobalsPtr;
  76.  
  77. /*****************************************************************************/
  78.  
  79. /* static prototypes */
  80.  
  81. static    void    GetLevelSize(long currentDirID,
  82.                              PreflightGlobalsPtr theGlobals);
  83.  
  84. static    OSErr    PreflightDirectoryCopySpace(short srcVRefNum,
  85.                                             long srcDirID,
  86.                                             short dstVRefNum,
  87.                                             Boolean *spaceOK);
  88.  
  89. static    void    CopyLevel(long sourceDirID,
  90.                           long dstDirID,
  91.                           EnumerateGlobalsPtr theGlobals);
  92.                           
  93. /*****************************************************************************/
  94.  
  95. static    void    GetLevelSize(long currentDirID,
  96.                              PreflightGlobalsPtr theGlobals)
  97. {
  98.     short    index = 1;    
  99.     do {
  100.           theGlobals->myCPB.dirInfo.ioFDirIndex = index;
  101.           theGlobals->myCPB.dirInfo.ioDrDirID = currentDirID;    /* we need to do this every time */
  102.                                                                   /* through, since GetCatInfo  */
  103.                                                                   /* returns ioFlNum in this field */
  104.           theGlobals->result = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->myCPB);
  105.           if (theGlobals->result == noErr) {
  106.              if ((theGlobals->myCPB.dirInfo.ioFlAttrib & ioDirMask) != 0) {
  107.                 /* we have a directory */
  108.     
  109.                 GetLevelSize(theGlobals->myCPB.dirInfo.ioDrDirID, theGlobals); /* recurse */
  110.                 theGlobals->result = noErr; /* clear error return on way back */
  111.             }
  112.             else {
  113.                 /* we have a file - add its allocation blocks to allocBlksNeeded */
  114.                     
  115.                 /* get number of 512-byte blocks needed for data fork */
  116.                 theGlobals->tempBlocks = (theGlobals->myCPB.hFileInfo.ioFlLgLen % 512) ?
  117.                                             ((theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9) + 1) :
  118.                                             (theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9);
  119.                 /* now, calculate number of new allocation blocks needed */
  120.                 theGlobals->allocBlksNeeded += (theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk) ?
  121.                                                 ((theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1) :
  122.                                                 (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk);
  123.             
  124.                 /* get number of 512-byte blocks needed for resource fork */
  125.                 theGlobals->tempBlocks = (theGlobals->myCPB.hFileInfo.ioFlRLgLen % 512) ?
  126.                                             ((theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9) + 1) :
  127.                                             (theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9);
  128.                 /* now, calculate number of new allocation blocks needed */
  129.                 theGlobals->allocBlksNeeded += (theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk) ?
  130.                                                 ((theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1) :
  131.                                                 (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk);
  132.             }
  133.         }
  134.         ++index;
  135.     } while (theGlobals->result == noErr);
  136. }
  137.  
  138. /*****************************************************************************/
  139.  
  140. static    OSErr    PreflightDirectoryCopySpace(short srcVRefNum,
  141.                                             long srcDirID,
  142.                                             short dstVRefNum,
  143.                                             Boolean *spaceOK)
  144. {
  145.     HParamBlockRec pb;
  146.     OSErr error;
  147.     long dstFreeBlocks;
  148.     PreflightGlobals theGlobals;
  149.     
  150.     /* Get the number of 512 byte blocks per allocation block and */
  151.     /* number of free allocation blocks on the destination volume */
  152.     pb.volumeParam.ioVRefNum = dstVRefNum;
  153.     pb.volumeParam.ioNamePtr = nil;
  154.     pb.volumeParam.ioVolIndex = 0;        /* use ioVRefNum only */
  155.     error = PBHGetVInfoSync(&pb);
  156.     if (error == noErr)
  157.     {
  158.         dstFreeBlocks = pb.volumeParam.ioVFrBlk;
  159.         
  160.         /* get allocation block size (always multiple of 512) and divide by 512
  161.           to get number of 512-byte blocks per allocation block */
  162.         theGlobals.dstBlksPerAllocBlk = (pb.volumeParam.ioVAlBlkSiz >> 9);
  163.         
  164.         theGlobals.allocBlksNeeded = 0;
  165.  
  166.         theGlobals.myCPB.dirInfo.ioNamePtr = theGlobals.itemName;
  167.         theGlobals.myCPB.dirInfo.ioVRefNum = srcVRefNum;
  168.         
  169.         
  170.         GetLevelSize(srcDirID, &theGlobals);
  171.         
  172.         /* Is there enough room on the destination volume for the source file? */
  173.         *spaceOK = (theGlobals.allocBlksNeeded <= dstFreeBlocks);
  174.     }
  175.     return (error);
  176. }
  177.  
  178. /*****************************************************************************/
  179.  
  180. static    void    CopyLevel(long sourceDirID,
  181.                           long dstDirID,
  182.                           EnumerateGlobalsPtr theGlobals)
  183. {
  184.     long currentSrcDirID;
  185.     long newDirID;
  186.     short index = 1;
  187.     
  188.     do
  189.     {    /* Isn't C great... What I'd give for a "WITH theGlobals DO" about now... */
  190.     
  191.         /* Get next source item at the current directory level */
  192.         
  193.         theGlobals->myCPB.dirInfo.ioFDirIndex = index;
  194.         theGlobals->myCPB.dirInfo.ioDrDirID = sourceDirID;
  195.         theGlobals->error = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->myCPB);        
  196.  
  197.         if (theGlobals->error == noErr) {
  198.         
  199.             /* We have an item.  Is it a file or directory? */
  200.             if (theGlobals->myCPB.hFileInfo.ioFlAttrib & 0x10) {
  201.             
  202.                 /* we have a directory */
  203.                 
  204.                 /* create a new directory at the destination. No errors allowed! */
  205.                 theGlobals->error = DirCreate(theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName, &newDirID);
  206.                 
  207.                 if (theGlobals->error == noErr) {
  208.                 
  209.                     /* Save the current source directory ID where we can get it when we come back
  210.                     ** from recursion land. */
  211.                     
  212.                     currentSrcDirID = theGlobals->myCPB.dirInfo.ioDrDirID;
  213.                     
  214.                     /* Dive again (copy the directory level we just found below this one) */
  215.                     
  216.                     CopyLevel(theGlobals->myCPB.dirInfo.ioDrDirID, newDirID, theGlobals);
  217.                     
  218.                     if (!theGlobals->bailout) {
  219.                     
  220.                         /* Copy comment from old to new directory. */
  221.                         
  222.                         theGlobals->error = CopyComment(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, nil, theGlobals->destinationVRefNum, newDirID, nil);
  223.                         
  224.                         /* handle any errors from CopyComment */
  225.                         if ((theGlobals->error != noErr) && (theGlobals->errorHandler != nil))
  226.                             theGlobals->bailout =  (* (theGlobals->errorHandler)) (theGlobals->error, copyDirCommentOp,
  227.                                                                                 theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, nil,
  228.                                                                                 theGlobals->destinationVRefNum, newDirID, nil);
  229.                     }
  230.  
  231.                     if (!theGlobals->bailout) {
  232.                     
  233.                         /* Copy directory attributes (dates, etc.) to newDirID. */
  234.                         
  235.                         theGlobals->error = CopyFileMgrAttributes(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, nil, theGlobals->destinationVRefNum, newDirID, nil, true);
  236.                         
  237.                         /* handle any errors from CopyFileMgrAttributes */
  238.                         if ((theGlobals->error != noErr) && (theGlobals->errorHandler != nil))
  239.                             theGlobals->bailout =  (* (theGlobals->errorHandler)) (theGlobals->error, copyDirFMAttributesOp,
  240.                                                                                 theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, nil,
  241.                                                                                 theGlobals->destinationVRefNum, newDirID, nil);
  242.                     }
  243.                 }
  244.                 else    /* error handling for DirCreate */
  245.                     if (theGlobals->errorHandler != nil)
  246.                         theGlobals->bailout = (* (theGlobals->errorHandler)) (theGlobals->error, dirCreateOp,
  247.                                                     theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, nil,
  248.                                                     theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName);
  249.                 theGlobals->error = noErr;    /* clear error return on way back */
  250.             }
  251.             else {
  252.             
  253.                 /* We have a file, so copy it */
  254.                 
  255.                 theGlobals->error = FileCopy(theGlobals->myCPB.hFileInfo.ioVRefNum,
  256.                                              theGlobals->myCPB.hFileInfo.ioFlParID,
  257.                                              theGlobals->itemName,
  258.                                              theGlobals->destinationVRefNum,
  259.                                              dstDirID,
  260.                                              nil,
  261.                                              nil,
  262.                                              theGlobals->copyBuffer,
  263.                                              theGlobals->bufferSize,
  264.                                              false);
  265.                         
  266.                 /* handle any errors from FileCopy */
  267.                 if ((theGlobals->error != noErr) && (theGlobals->errorHandler != nil))
  268.                     theGlobals->bailout =  (* (theGlobals->errorHandler)) (theGlobals->error, fileCopyOp,
  269.                                                                         theGlobals->myCPB.hFileInfo.ioVRefNum, theGlobals->myCPB.hFileInfo.ioFlParID, theGlobals->itemName,
  270.                                                                         theGlobals->destinationVRefNum, dstDirID, nil);
  271.             }
  272.         }
  273.         else {    /* error handling for PBGetCatInfo */
  274.             /* it's normal to get a fnfErr when indexing; that only means you've hit the end of the directory */
  275.             if ((theGlobals->error != fnfErr) && (theGlobals->errorHandler != nil)) { 
  276.                 theGlobals->bailout = (* (theGlobals->errorHandler)) (theGlobals->error, getNextItemOp, theGlobals->myCPB.dirInfo.ioVRefNum, sourceDirID, nil, 0, 0, nil);
  277.             }
  278.         }
  279.         ++index; /* prepare to get next item */
  280.     }
  281.     while ((theGlobals->error == noErr) && (!theGlobals->bailout)); /* time to fall back a level? */
  282. }
  283.  
  284. /*****************************************************************************/
  285.  
  286. pascal    OSErr    DirectoryCopy(short srcVRefNum,
  287.                               long srcDirID,
  288.                               StringPtr srcName,
  289.                               short dstVRefNum,
  290.                               long dstDirID,
  291.                               StringPtr dstName,
  292.                               Ptr copyBufferPtr,
  293.                               long copyBufferSize,
  294.                               Boolean preflight,
  295.                               CopyErrProcPtr copyErrHandler)
  296. {
  297.     EnumerateGlobals theGlobals;
  298.     Boolean    isDirectory;
  299.     OSErr    error;
  300.     Boolean ourCopyBuffer = false;
  301.     Str63    srcDirName;
  302.     Boolean spaceOK;
  303.     
  304.     /* Make sure a copy buffer is allocated. */
  305.     if (copyBufferPtr == nil) {
  306.         /* The caller didn't supply a copy buffer so grab one from the application heap.
  307.         ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer.
  308.         ** If 512 bytes aren't available, we're in trouble. */
  309.         copyBufferSize = dirCopyBigCopyBuffSize;
  310.         copyBufferPtr = NewPtr(copyBufferSize);
  311.         if (copyBufferPtr == nil) {
  312.             copyBufferSize = dirCopyMinCopyBuffSize;
  313.             copyBufferPtr = NewPtr(copyBufferSize);
  314.             if (copyBufferPtr == nil)
  315.                 return(MemError());
  316.         }
  317.         ourCopyBuffer = true;
  318.     }
  319.     
  320.     /* Get the real dirID where we're copying from and make sure it is a directory. */
  321.     error = GetDirID(srcVRefNum, srcDirID, srcName, &srcDirID, &isDirectory);
  322.     if (error != noErr)
  323.         goto ErrorExit;
  324.     if (!isDirectory) {
  325.         error =  dirNFErr;
  326.         goto ErrorExit;
  327.     }
  328.         
  329.     /*  Get the real dirID where we're going to put the copy and make sure it is a directory. */
  330.     error = GetDirID(dstVRefNum, dstDirID, dstName, &dstDirID, &isDirectory);
  331.     if (error != noErr)
  332.         goto ErrorExit;
  333.     if (!isDirectory) {
  334.         error =  dirNFErr;
  335.         goto ErrorExit;
  336.     }
  337.     
  338.     /* Get the real vRefNum of both the source and destination */
  339.     error = DetermineVRefNum(srcName, srcVRefNum, &srcVRefNum);
  340.     if (error != noErr)
  341.         goto ErrorExit;
  342.     error = DetermineVRefNum(dstName, dstVRefNum, &dstVRefNum);
  343.     if (error != noErr)
  344.         goto ErrorExit;
  345.     
  346.     if (preflight) {
  347.         error = PreflightDirectoryCopySpace(srcVRefNum, srcDirID, dstVRefNum, &spaceOK);
  348.         if (error != noErr)
  349.             goto ErrorExit;
  350.         if (!spaceOK) {
  351.             error = dskFulErr; /* not enough room on destination */
  352.             goto ErrorExit;
  353.         }
  354.     }
  355.  
  356.     /* Create the new directory in the destination directory with the */
  357.     /* same name as the source directory. */
  358.     error = GetDirName(srcVRefNum, srcDirID, srcDirName);
  359.     if (error != noErr)
  360.         goto ErrorExit;
  361.     error = DirCreate(dstVRefNum, dstDirID, srcDirName, &dstDirID);
  362.     if (error != noErr)
  363.         goto ErrorExit;
  364.     
  365.     /* dstDirID is now the newly created directory! */
  366.         
  367.     /* Set up the globals we need to access from the recursive routine. */
  368.     theGlobals.copyBuffer = copyBufferPtr;
  369.     theGlobals.bufferSize = copyBufferSize;
  370.     theGlobals.destinationVRefNum = dstVRefNum; /* so we can get to it always */
  371.     theGlobals.myCPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName;
  372.     theGlobals.myCPB.hFileInfo.ioVRefNum = srcVRefNum;
  373.     theGlobals.errorHandler = copyErrHandler;
  374.     theGlobals.bailout = false;
  375.         
  376.     /* Here we go into recursion land... */
  377.     CopyLevel(srcDirID, dstDirID, &theGlobals);
  378.     
  379.     if (!theGlobals.bailout) {
  380.     
  381.         /* Copy comment from source to destination directory. */
  382.         
  383.         error = CopyComment(srcVRefNum, srcDirID, nil, dstVRefNum, dstDirID, nil);
  384.         
  385.         /* handle any errors from CopyComment */
  386.         if ((error != noErr) && (copyErrHandler != nil))
  387.             theGlobals.bailout =  (* copyErrHandler) (error, copyDirCommentOp,
  388.                                     srcVRefNum, srcDirID, nil,
  389.                                     dstVRefNum, dstDirID, nil);
  390.     }
  391.  
  392.     if (!theGlobals.bailout) {
  393.     
  394.         /* Copy the File Manager attributes */
  395.         
  396.         error = CopyFileMgrAttributes(srcVRefNum, srcDirID, nil,
  397.                     dstVRefNum, dstDirID, nil, true);
  398.         
  399.         /* handle any errors from CopyFileMgrAttributes */
  400.         if ((error != noErr) && (copyErrHandler != nil))
  401.             theGlobals.bailout = (* copyErrHandler) (error, copyDirFMAttributesOp,
  402.                                     srcVRefNum, srcDirID, nil,
  403.                                     dstVRefNum, dstDirID, nil);
  404.     }
  405.  
  406. ErrorExit:
  407.     /* Get rid of the copy buffer if we allocated it. */
  408.     if (ourCopyBuffer)
  409.         DisposPtr(copyBufferPtr);
  410.  
  411.     return (error);
  412. }
  413.  
  414. /*****************************************************************************/
  415.  
  416. pascal    OSErr    FSpDirectoryCopy(const FSSpec *srcSpec,
  417.                                  const FSSpec *dstSpec,
  418.                                  Ptr copyBufferPtr,
  419.                                  long copyBufferSize,
  420.                                  Boolean preflight,
  421.                                  CopyErrProcPtr copyErrHandler)
  422. {
  423.     return (DirectoryCopy(srcSpec->vRefNum, srcSpec->parID, (StringPtr)srcSpec->name,
  424.                           dstSpec->vRefNum, dstSpec->parID, (StringPtr)dstSpec->name,
  425.                           copyBufferPtr, copyBufferSize, preflight, copyErrHandler));
  426. }
  427.  
  428. /*****************************************************************************/
  429.  
  430.